//! Headers which are parts of application.
#include "HDRLightsDemo.hpp"
#include "HDRDemoConstants.hpp"
#include "Extensions/ExtensionsLoader.hpp"
#include "ScreenSystem/DemoSceneScreen.hpp"
#include "ScreenSystem/ScreenManager.hpp"
#include "SceneManager.hpp"
#include "Mouse/MouseManager.hpp"
#include "ResourceManagers/TextureManager.hpp"

//! Headers for asserts and time and math functions.
#include <time.h>
#include <math.h>
#include <assert.h>

//! Headers for external functionality ( text files loading for shaders /
//! FreeImage library providing loading of graphic files like .PNG, .BMP, etc. ).
#include "ResourceManagers/textfile.h"
#include "ResourceManagers/FreeImage.h"

//! Headers concerning OpenGL.
//#include <GL/glut.h>
//#include <GL/glext.h>

using namespace std;

#define _NO_SHADERS
//#define MULTIPLE_LIGHTS_ON_REGISTERS


HDRLightsDemo::HDRLightsDemo(int argc, char **argv):
m_angle( 0.0f ),
m_x( 0.0f ),
m_y( 15.0f ),
m_z( 20.0f )
{
	m_argc = argc;
	m_argv = argv;

	InitGLUT();
	InitDemo();
}

HDRLightsDemo::~HDRLightsDemo()
{
	SceneManager::GetInstance()->UnregisterAllEntities();
	delete m_demoScene;
}

bool HDRLightsDemo::InitDemo()
{
	//! Create ExtensionsLoader.
	ExtensionsLoader::GetInstance();

	//! Set light parameters.
	GLfloat LightAmbient[] = { 0.2f, 0.2f, 0.2f, 1.0f },
			LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f },
			LightSpecular[] = { 1.0f, 1.0f, 1.0f, 1.0f },
			LightPosition[] = { 10.0f, 5.0f, 15.0f };

	glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient );
	glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse );
	glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular );
	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition );

	//! Initialise FreeImage library with using only local plugins.
	FreeImage_Initialise( true );

	//! Create managers.
	SceneManager::GetInstance();
	CameraManager::GetInstance();
	ScreenManager::GetInstance();
	MouseManager::GetInstance();
	TextureManager::GetInstance();

	//! Create screen.
	m_demoScene = new DemoSceneScreen;

#ifndef _NO_SHADERS
	LoadShaders();
#endif

	return true;
}

void HDRLightsDemo::InitGLUT()
{
	//! Initialises GLUT, enables some OpenGL states and sets callback functions.
	glutInit( &m_argc, m_argv );
	glutInitDisplayMode( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA );

	glutInitWindowPosition( WINDOW_X_POS, WINDOW_Y_POS );
	glutInitWindowSize( WINDOW_WIDTH , WINDOW_HEIGHT );
	glutCreateWindow( WINDOW_CAPTION );

	glutMouseFunc( &MouseManager::GetInstance()->ProcessMouseClicks );
	glutMotionFunc( &MouseManager::GetInstance()->ProcessMouseMotion );

	glEnable( GL_DEPTH_TEST );

	//glutDisplayFunc( &SceneManager::GetInstance()->UpdateScene );
	//glutIdleFunc( &SceneManager::GetInstance()->UpdateScene );
	glutDisplayFunc( &UpdateDemo );
	glutIdleFunc( &UpdateDemo );

	glutReshapeFunc( &HDRLightsDemo::ResizeWindow );

	//glutDisplayFunc( &SceneManager::GetInstance()->UpdateScene );
	glutDisplayFunc( &UpdateDemo );

#ifdef _NO_SHADERS
	glEnable( GL_LIGHTING );
	glEnable( GL_LIGHT0 );
	glShadeModel( GL_SMOOTH );
#endif

}

void HDRLightsDemo::GoSleep( clock_t wait )
{
	//! Puts application into sleep for specified time.
	clock_t goal;
	goal = wait + clock();
	while( goal > clock() )
	;
}
void HDRLightsDemo::LoadShaders()
{
	//! Loads shaders from external text file.

	char *vs = NULL, *fs = NULL;
	int maxFragmentALUOps;

	m_v  = ExtensionsLoader::glCreateShaderObjectARB( GL_VERTEX_SHADER_ARB );
	m_f  = ExtensionsLoader::glCreateShaderObjectARB( GL_FRAGMENT_SHADER_ARB);

	//sprawdzenie ilosci operacji ALU wykonywanych prze karte w jednym programie
	ExtensionsLoader::glGetProgramivARB( GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB, &maxFragmentALUOps );
	printf("GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB: %d\n", maxFragmentALUOps );

	vs  = textFileRead( VERTEX_PROGRAM_SOURCE );
	fs  = textFileRead( FRAGMENT_PROGRAM_SOURCE );

	const char * vv  = vs;
	const char * ff  = fs;

	ExtensionsLoader::glShaderSourceARB( m_v,  1, &vv, NULL );
	ExtensionsLoader::glShaderSourceARB( m_f,  1, &ff, NULL );

	free( vs ); free( fs );

	//kompilacja shaderow
	ExtensionsLoader::glCompileShaderARB( m_v );
	ExtensionsLoader::glCompileShaderARB( m_f );

	PrintInfoLog( m_v );
	PrintInfoLog( m_f );

	m_shader = ExtensionsLoader::glCreateProgramObjectARB();
	ExtensionsLoader::glAttachObjectARB( m_shader, m_v );
	ExtensionsLoader::glAttachObjectARB( m_shader, m_f );

	ExtensionsLoader::glLinkProgramARB( m_shader );
	PrintInfoLog( m_shader );

	ExtensionsLoader::glUseProgramObjectARB( m_shader );


	srand( (unsigned)time( NULL ) );

	int index; //liczba swiatel
	float *lpos;
	GLint loc1, loc2;

#ifndef MULTIPLE_LIGHTS_ON_REGISTERS

	index = 1;
	lpos = new float[ 3 * index ];
	lpos[0] = 0.0f; lpos[1] = 5.0f; lpos[2] = 13.0f;

#else

	index = 150;
	lpos = new float[ 3 * index ];

	int i = 0;
	while( i < 3*index - 2 )
	{
		float u = (double)rand() / (RAND_MAX + 1) * (250 - (-250)) + (-250);
		lpos[i]   = u;

		u = (double)rand() / (RAND_MAX + 1) * (250 - (-250)) + (-250);
		lpos[i+1] = u;

		u = (double)rand() / (RAND_MAX + 1) * (250 - (-250)) + (-250);
		lpos[i+2] = u;

		cout.precision(3);
		cout << "L[" << i << "]:" << lpos[i]  << "   ";
		cout << "L[" << i+1 << "]:" << lpos[i+1] << "   ";
		cout << "L[" << i+2 << "]:" << lpos[i+2]  << "\n";

		i += 3;
		//GoSleep(200);
	}
#endif

	loc1 = ExtensionsLoader::glGetUniformLocationARB( m_shader,"lpos" );
	ExtensionsLoader::glUniform3fvARB( loc1, index,lpos );

	loc2 = ExtensionsLoader::glGetUniformLocationARB( m_shader, "index" );
	ExtensionsLoader::glUniform1iARB( loc2, index );

	delete[] lpos;

}

void HDRLightsDemo::CreateScene()
{

}

void HDRLightsDemo::PrintInfoLog( GLhandleARB obj )
{
	//! Prints log about shader's initialisation.

    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

	ExtensionsLoader::glGetObjectParameterivARB( obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength );

    if (infologLength > 0)
    {
        infoLog = ( char * )malloc( infologLength );
		ExtensionsLoader::glGetInfoLogARB( obj, infologLength, &charsWritten, infoLog );
		printf( "%s\n",infoLog );
        free( infoLog );
    }
}

void HDRLightsDemo::UpdateDemo()
{
	clock_t tick = clock();

	SceneManager::GetInstance()->UpdateScene( tick );
	CameraManager::GetInstance()->Update( tick );
}

//bool HDRLightsDemo::CheckIfShadersSupported()
//{
//	char *ext = ( char* )glGetString( GL_EXTENSIONS );
//	char *gl_ver = ( char* )glGetString( GL_VERSION );
//	char *glsl_v = ( char* )glGetString( GL_SHADING_LANGUAGE_VERSION );
//
//	int glutv = GLUT_API_VERSION;
//
//	std::cout << "OpenGL version: " << gl_ver << "\n";
//	std::cout << "GLSL version: " << glsl_v << "\n";
//	std::cout << "GLUT version: " << glutv << "\n";
//
//	if( ( strstr( ext, "GL_ARB_fragment_shader" ) != NULL ) && ( strstr( ext, "GL_ARB_vertex_shader" ) != NULL ) )
//	{
//		std::cout << "Shaders supported! Program will continue.\n";
//		return true;
//	}
//	else
//	{
//		std::cout << "Shaders not supported! You must have graphic card which have Vertex & Pixel Shader units.\n";
//	}
//
//	return false;
//}